/*
 * Copyright (c) 2011 Toni Spets <toni.spets@iki.fi>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define STATICLIB
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/miniwget.h>

#include "common.h"

struct upnp_thread_args { char addr[32]; char port[16]; };

void upnp_thread(struct upnp_thread_args *args)
{
    struct UPNPDev *devlist;
    struct UPNPDev *dev;
    char *descXML;
    int descXMLsize = 0;

    struct UPNPUrls urls;
    struct IGDdatas data;

    printf("upnp_thread: Searching for devices (%dms timeout)...\n", config_upnp_discover);

    devlist = upnpDiscover(config_upnp_discover, NULL, NULL, 0, NULL);

    if (devlist)
    {
        printf("upnp_thread: Devices found, searching for InternetGatewayDevice...\n");

        dev = devlist;
        while (dev)
        {
            if (strstr (dev->st, "InternetGatewayDevice"))
            {
                break;
            }

            dev = dev->pNext;
        }

        if (!dev)
        {
            dev = devlist;
        }

        printf("upnp_thread: Using device at %s, requesting for description...\n", dev->descURL);

        descXML = miniwget(dev->descURL, &descXMLsize);

        if (descXML)
        {
            int ret;

            printf("upnp_thread: Got XML document\n");

            parserootdesc(descXML, descXMLsize, &data);
            free(descXML);

            GetUPNPUrls(&urls, &data, dev->descURL);

            if(urls.controlURL[0])
            {
                printf("upnp_thread: Requesting portmapping for %s:%s\n", args->addr, args->port);
#if UPNP_FORCE
                UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, args->port, "UDP", NULL);
#endif
                ret = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, args->port, args->port, args->addr, "CnCNet", "UDP", NULL, NULL);

                if (ret != 0)
                {
                    printf("upnp_thread: Failed to get portmapping: %d, nooooo\n", ret);
                }
                else
                {
                    printf("upnp_thread: Port mapped!\n");
                }
            }
            else
            {
                printf("upnp_thread: Error: No UPnP URLs\n");
            }
        }
        else
        {
            printf("upnp_thread: Error requesting XML\n");
        }

        freeUPNPDevlist(devlist);
    }
}

HANDLE upnp_run(int port)
{
    char hostname[256];
    static struct upnp_thread_args args = { "", "" };
    struct hostent *he;

    if (!config_upnp)
    {
        return NULL;
    }

    net_init();

    gethostname(hostname, 256);
    he = gethostbyname(hostname);

    printf("upnp: local hostname: %s\n", hostname);

    if (he)
    {
        strcpy(args.addr, inet_ntoa(*(struct in_addr *)(he->h_addr_list[0])));
        snprintf(args.port, 16, "%d", port);

        printf("upnp: local ip: %s\n", args.addr);
        printf("upnp: port: %s\n", args.port);

        return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)upnp_thread, &args, 0, NULL);
    }
    else
    {
        printf("upnp: couldn't get the local ip address\n");
    }

    return NULL;
}

#ifdef UPNP_TEST
int main(int argc, char **argv)
{
    printf("CnCNet UPnP Test Program\n");
    WaitForSingleObject(upnp_run(8054), INFINITE);
    return 0;
}
#endif
